/*
 * arquivo.c
 *
 *  Created on: Jul 4, 2011
 *      Author: tualibano
 */

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <dirent.h>

#include "structs.h"
#include "configurar.h"
#include "menu.h"
#include "busca.h"
#include "arquivo.h"
#include "sha1.h"
//#include "sha1.c"
// string = "busca":nome:id:ip_local:porta 

int buscalocal(char *string) {
  //  puts("entrou em buscal local");
    char resposta[256], *nome, *ip;
    arquivo *ntabela;
    struct sockaddr_in ipremoto;
    int socketremoto, porta;
    unsigned int iplen = sizeof (struct sockaddr_in);
    // buscar se tem na tabela de arquivo
    //tratar string
    /*
     * acho o arquivo localmente?
     * manda via UDP: "arquivo":id_busca:iplocal:tamanho:pedaços:resto
     *
     */
    strtok(string, ":"); //"busca"
    strtok(NULL, ":"); //id
    nome = strtok(NULL, ":"); //nome
    ip = strtok(NULL, ":"); //ip_local
    strtok(NULL, ":"); //portatcp
    porta = atoi(strtok(NULL, ":")); //portaudp
    if ((socketremoto = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
        perror("socket");

    ipremoto.sin_family = AF_INET;
    ipremoto.sin_port = htons(porta);
    ipremoto.sin_addr.s_addr = inet_addr(ip);
    bzero(&(ipremoto.sin_zero), 8);
    ntabela = tabelaarquivo;


    while (strcmp(ntabela->sha1, "")) {
        if (strstr(ntabela->nome, nome)) {
            sprintf(resposta, "arquivo:%s:%s:%d:%d:%d:%s:%s", nome, nome,
                    ntabela->tamanho, ntabela->ciclos, ntabela->resto, iplocal, ntabela->sha1);
            sendto(socketremoto, resposta, 256, 0, (struct sockaddr*) &ipremoto, iplen);
        }
        ntabela++;
    }
   // puts("saiu de buscal local");
    return 0;
}

int listararquivos(arquivo **tabela) {
    arquivo *ntabela;
    FILE *temp;
    int total_arquivos, i, len;
    struct dirent **namelist;

    total_arquivos = scandir(".", &namelist, 0, alphasort);

    ntabela = (arquivo *) malloc((total_arquivos - 4) * sizeof (arquivo));
    (*tabela) = ntabela;

    if (total_arquivos < 0)
        perror("scandir");
    else {
        for (i = 2; i < total_arquivos; i++) {
            sprintf(ntabela->nome, "%s\n", namelist[i]->d_name);
            len = strlen(ntabela->nome);
            ntabela->nome[len - 1] = '\0';
            temp = fopen(ntabela->nome, "r");
            fseek(temp, 0, SEEK_END);
            ntabela->tamanho = ftell(temp);
            fseek(temp, 0, SEEK_SET);
            sha1(ntabela->nome, ntabela->sha1);
            ntabela->ciclos = ntabela->tamanho / TAM_REAL;
            ntabela->resto = ntabela->tamanho % TAM_REAL;
            free(namelist[i]);
            ntabela++;
        }
        free(namelist);
    }

    //(*tabela) = ntabela;
    return 0;
}

int preenche(respostabusca **tabelabusca) {
    respostabusca *ntabela;
    FILE *temp;
    int total_arquivos, i = 2, len;
    struct dirent **namelist;

    total_arquivos = scandir(".", &namelist, 0, alphasort);
    ntabela = (respostabusca *) malloc((total_arquivos - 4) * sizeof (respostabusca));
    (*tabelabusca) = ntabela;

    if (total_arquivos < 0)
        perror("scandir");
    else {
        for (i = 2; i < total_arquivos; i++) {
            sprintf(ntabela->idbusca, "%s\n", namelist[i]->d_name);
            len = strlen(ntabela->idbusca);
            ntabela->idbusca[len - 1] = '\0';
            temp = fopen(ntabela->idbusca, "r");
            fseek(temp, 0, SEEK_END);
            ntabela->tamanho = ftell(temp);
            fseek(temp, 0, SEEK_SET);
            sha1(ntabela->idbusca, ntabela->sha1);
            ntabela->pedacos = ntabela->tamanho / TAM_REAL;
            ntabela->resto = ntabela->tamanho % TAM_REAL;
            strcpy(ntabela->quemtemarquivo.Ip, "127.0.0.1");
            ntabela->quemtemarquivo.PortaTcp = 5001;
            ntabela->quemtemarquivo.PortaUdp = 5001;
            ntabela++;
            free(namelist[i]);
            //  printf("Nome: %s, Tamanho: %d, SHA1: %s\n", ntabela->idbusca, ntabela->tamanho, ntabela->sha1);
        }
        free(namelist);
    }

    //(*tabelabusca) = ntabela;
    return 0;
}

int criartabelabusca(respostabusca **tabela) {
    respostabusca *ntabela;
    respostabusca *ztabela;
    int i;

    ntabela = (respostabusca *) malloc(TAMANHO_TABELA_RESP_BUSCA
            * sizeof (respostabusca));

    ztabela = ntabela;
    for (i = 0; i < TAMANHO_TABELA_RESP_BUSCA; i++) {
        zerarbusca(&ztabela);
        ztabela++;
    }

    (*tabela) = ntabela;
    return 0;
}

int inserirtabelabusca(respostabusca **tabela, respostabusca *novoelemento) {
    respostabusca *ntabela = *tabela;
    int contador = 0;

    while (ntabela[contador].pedacos > 0)
        contador++;
    strcpy(ntabela[contador].sha1, novoelemento->sha1);
    strcpy(ntabela[contador].idbusca, novoelemento->idbusca);
    ntabela[contador].pedacos = novoelemento->pedacos;
    ntabela[contador].tamanho = novoelemento->tamanho;
    ntabela[contador].resto = novoelemento->resto;
    strcpy(ntabela[contador].quemtemarquivo.Ip, novoelemento->quemtemarquivo.Ip);
    ntabela[contador].quemtemarquivo.Latencia
            = 0;
    ntabela[contador].quemtemarquivo.Pontos
            = 0;
    ntabela[contador].quemtemarquivo.PortaTcp
            = 5001;
    ntabela[contador].quemtemarquivo.PortaUdp
            = 5001;
    return 0;
}

int removertabelabusca(respostabusca **tabela, respostabusca *elemento) {

    return 0;
}

int printtabelabusca() {
    respostabusca *ntabela;
    ntabela = tabelabusca;
    int i = 0;
    system("clear");
    printf("%s\n", " -----------------------------------------------------------------------------");
    printf("|%3s |%10s %5c |%23s %20c |%8s|\n", "#", "Nome", ' ', "SHA1", ' ', "Tamanho");
    printf("%s\n", " -----------------------------------------------------------------------------");
    while (ntabela->tamanho > 0) {
        printf("|%3d |%16s |%44s |%8d|\n", i,
                ntabela->idbusca, ntabela->sha1, ntabela->tamanho);
        ntabela++;
        i++;
    }
    printf("%s\n", " -----------------------------------------------------------------------------");
    printf("\n");
    return 0;
}

int printtabelaarquivo() {
    arquivo *ntabela;
    ntabela = tabelaarquivo;
    int i = 0;


    while (ntabela->tamanho > 0) {
        printf("Nome: %s, Tamanho: %d, SHA1: %s, resto: %d\n, partes: %d",
                ntabela->nome, ntabela->tamanho, ntabela->sha1, ntabela->resto, ntabela->ciclos);
        ntabela++;
        i++;
    }
    printf("total: %i\n", i);
    return 0;
}

respostabusca *buscartabelabusca(respostabusca **tabela, char *nomearquivo) {
    int contador;
    respostabusca *ntabela = *tabela;

    while (ntabela[contador].pedacos > 0) {

        contador++;
    }

    return *tabela;
}

int zerarbusca(respostabusca **elemento) {
    strcpy((*elemento)->idbusca, "");
    (*elemento)->pedacos = 0;
    ZerarVizinho(&((*elemento)->quemtemarquivo));
    (*elemento)->tamanho = 0;
    (*elemento)->resto = 0;
    return 0;
}

void *cp(void *ptr) {
    paramT *copia = (paramT *) ptr;
    FILE *temp;
    int numbytes, somabytes = 0;
    char pacoteBuffer[TAM_BUFFER + 64], nPacote[64], sBuffer[TAM_REAL];

    //int buscartabelabusca(nomearquivo);
    //abrir uma conexão
    //send: nome_arquivo:pedaço:resto:"arquivo"
    sprintf(nPacote, "arquivo:%d:%s", copia->pedaco, copia->arquivo.sha1);
    send(copia->socket, nPacote, 64, 0);
    temp = fopen(copia->arquivo.nome, "r+");

    fseek(temp, TAM_REAL * copia->pedaco, SEEK_SET);
    memset(pacoteBuffer, 1, TAM_BUFFER);
    while (somabytes < TAM_REAL) {
        numbytes = recv(copia->socket, sBuffer, TAM_BUFFER, 0);
        somabytes = somabytes + numbytes;
        fwrite(sBuffer, numbytes, 1, temp);
    }
    fclose(temp);
    pthread_exit((void*) 0);

}
//pedaco:sha1

int enviararquivo(int socket, char *string) {
    FILE *arquivomandar;
    arquivo *baixar;
    int pacote;
    char dados[TAM_REAL], *sha1;
    baixar = tabelaarquivo;
    strtok(string, ":"); //"arquivo"
    pacote = atoi(strtok(NULL, ":")); //pedaco
    sha1 = strtok(NULL, ":"); //sha1

    while (strcmp(baixar->nome, "")) {
        if ((strstr(sha1, baixar->sha1)))
            break;
        baixar++;
    }

   // puts("check1");
    arquivomandar = fopen(baixar->nome, "r");
    puts(baixar->nome);
    //puts("check2");
    /*Enviar pacote*/
    fseek(arquivomandar, pacote * TAM_REAL, SEEK_SET);
   // puts("check3");
    fread(dados, TAM_REAL, 1, arquivomandar);
  //  puts("check4");
    //puts(dados);
    send(socket, dados, TAM_BUFFER, 0);
    return 0;
}

int sha1(char *arquivo, char *sha1) {
    int numbytes;
    FILE *arquivosha1;
    SHA1Context sha;
    char *dados;
    printf("Calculando SHA1 %s...\n", arquivo);
    arquivosha1 = fopen(arquivo, "r");

    fseek(arquivosha1, 0, SEEK_END);
    numbytes = ftell(arquivosha1);
    fseek(arquivosha1, 0, SEEK_SET);

    dados = (char *) malloc(numbytes * sizeof (char));
    fread(dados, numbytes, 1, arquivosha1);
    SHA1Reset(&sha);
    SHA1Input(&sha, (const unsigned char *) dados, numbytes);
    sprintf(sha1, "%x %x %x %x %x", sha.Message_Digest[0], sha.Message_Digest[1],
            sha.Message_Digest[2], sha.Message_Digest[3], sha.Message_Digest[4]);
    fclose(arquivosha1);
    free(dados);
    return 0;
}

int baixa(int index) {
    FILE *arquivoreceber;
    arquivo baixar;
    respostabusca *atabelabusca = tabelabusca;
    respostabusca osqtem[16];
    char ip[16], sha1[60], presto[TAM_BUFFER], sBuffer[TAM_BUFFER];
    struct sockaddr_in servidor;
    int i = 0, porta, sockets[16], j, n = 0, check = 1,
            vArquivo[NUM_THREAD], ciclos, somabytes = 0, numbytes;
    pthread_t thread[NUM_THREAD];
    paramT argThreads[NUM_THREAD];
    strcpy(sha1, tabelabusca[index].sha1);

    while (strcmp(atabelabusca->sha1, "")) {
        if (!(strcmp(sha1, atabelabusca->sha1))) {
            osqtem[i] = *atabelabusca;
            i++;
        }
        atabelabusca++;
    }

    baixar.ciclos = osqtem[0].pedacos;
    strcpy(baixar.nome, osqtem[0].idbusca);
    baixar.resto = osqtem[0].resto;
    strcpy(baixar.sha1, osqtem[0].sha1);
    baixar.tamanho = osqtem[0].tamanho;
    
    arquivoreceber = fopen(baixar.nome, "w");

    for (j = 0; j < i; j++) {
        //Faz conexão
        porta = osqtem[j].quemtemarquivo.PortaTcp;
        if ((sockets[j] = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
            perror("socket");
            exit(1);
        }

        strcpy(ip, osqtem[j].quemtemarquivo.Ip);

        servidor.sin_family = AF_INET;
        servidor.sin_port = htons(porta);
        servidor.sin_addr.s_addr = inet_addr(ip);
        bzero(&(servidor.sin_zero), 8);

        if (connect(sockets[j], (struct sockaddr *) &servidor,
                sizeof (servidor)) == -1) {
            close(sockets[j]);
        }

    }
    ciclos = osqtem[0].pedacos;
    if (baixar.ciclos) {
        for (j = 0; j < ciclos; j++) {
            vArquivo[j] = 1;
        }
        j = 0;

        while (check) {
            argThreads[j].arquivo = baixar;
            argThreads[j].pedaco = j;
            if (n == i)
                n = 0;

            argThreads[j].socket = sockets[n];
            if (vArquivo[j]) {
                pthread_create(&thread[j], NULL, cp, (void*) &argThreads[j]);
                pthread_join(thread[j], (void **) &vArquivo[j]);
                n++;
            };

            j++;
            if (j == ciclos) {
                check = 0;
                for (j = 0; j < ciclos; j++) {
                    check = check + vArquivo[j];
                }
                j = 0;

            }
        }
    }
    if (baixar.resto) {
        sprintf(presto, "resto:%s", baixar.sha1);
        send(sockets[0], presto, 64, 0);

        fseek(arquivoreceber, TAM_REAL * ciclos, SEEK_SET);
        //printf("resto: %d\n", baixar.resto);
        while (somabytes != baixar.resto) {

            numbytes = recv(sockets[0], sBuffer, TAM_BUFFER, 0);
            somabytes = somabytes + numbytes;
           // puts(sBuffer);
            fwrite(sBuffer, numbytes, 1, arquivoreceber);
        }

    }

    for (j = 0; j < i; j++) {
        close(sockets[j]);
    }
    fclose(arquivoreceber);
    return 0;
}
//string resto:nome

int resto(int socket, char *string) {
    FILE *arquivomandar;
    arquivo *baixar;
    char dados[TAM_REAL], *sha1;
    baixar = tabelaarquivo;
    strtok(string, ":"); //"resto"
    sha1 = strtok(NULL, ":"); //sha1

    while (strcmp(baixar->nome, "")) {
        if ((strstr(sha1, baixar->sha1)))
            break;
        baixar++;
    }
   // printf("resto: %d\n", baixar->resto);
    puts(baixar->nome);
    arquivomandar = fopen(baixar->nome, "r");
    /*Enviar pacote*/
    fseek(arquivomandar, baixar->ciclos * TAM_REAL, SEEK_SET);
    fread(dados, baixar->resto, 1, arquivomandar);
   // puts(dados);

    send(socket, dados, baixar->resto, 0);

    return 0;
}
